#ifndef XG_WEBX_STD_H
#define XG_WEBX_STD_H
///////////////////////////////////////////////////////////
#include <functional>
#include <json/json.h>
#include <zlib/ZipPacker.h>
#include <http/HttpServer.h>
#include <http/HttpRequest.h>
#include <dbx/RedisConnect.h>

#ifndef HTTP_GZIP_SIZE
#define HTTP_GZIP_SIZE  (10 * 1024)
#endif

namespace webx
{
	class Application;

	class FileParamItem
	{
	public:
		int sz;
		string key;
		string contype;
		string filename;
		string filepath;
		const char* data;
	};

	class ProcessBase : public HttpProcessBase
	{
		friend class Application;

	protected:
		string user;
		string dbid;
		HttpServer* app;
		JsonElement json;
		sp<Session> session;
		function<int()> func;
		HttpRequest* request;
		HttpResponse* response;

	public:
		virtual int process();
		virtual int forward(ProcessBase* cgi);
		virtual int simpleResponse(int code = XG_OK);
		virtual void checkLogin(const string& sid = "");
		virtual void checkSession(const string& sid = "");
		virtual void parse(JsonReflect& data, bool decode = false);
		virtual void checkSystemRight(const string& grouplist = "");
		virtual int doWork(HttpRequest* request, HttpResponse* response);

	public:
		sp<Session> getSession() const
		{
			return session;
		}
		HttpRequest* getRequest() const
		{
			return request;
		}
		HttpResponse* getResponse() const
		{
			return response;
		}
		ProcessBase() : app(HttpServer::Instance()), request(NULL), response(NULL)
		{
		}
	};
	
	class Application : public ::Application
	{
	protected:
		string user;
		string dbid;
		sp<MemFile> file;
		JsonElement json;
		IHttpServer* app;
		StringCreator out;
		function<int()> func;

	public:
		virtual bool main();
		virtual int doWork();
		virtual int process();
		virtual void checkLogin();
		virtual int forward(ProcessBase* cgi);
		virtual void checkLogin(JsonElement& data);
		virtual int simpleResponse(int code = XG_OK);
		virtual void parse(JsonReflect& data, bool decode = false);
		virtual void checkSystemRight(const string& grouplist = "");

	public:
		void clearResponse()
		{
			out.clear();
			file = NULL;
		}
		void createFile(int maxsz)
		{
			sp<MemFile> file = newsp<MemFile>();

			if (file->create(maxsz))
			{
				this->file = file;
			}
			else
			{
				stdx::Throw(XG_SYSERR, "create memory file failed");
			}
		}
	};

	class WebAppData
	{
	public:
		string path;
		function<sp<ProcessBase>()> create;

		WebAppData()
		{
		}
		WebAppData(const string& path, function<sp<ProcessBase>()> create)
		{
			this->path = path;
			this->create = create;
		}
	};

	string GetCgiPathList();
	void ReloadSystemConfig();
	map<string, WebAppData>* GetAppMap();
	sp<ProcessBase> GetWebApp(const string& key);
	string GetAppHeadParameter(const string& key);
	string GetAppDataParameter(const string& key);

	bool IsMailString(const string& str);
	string GetFileIcon(const string& filename);
	string GetSessionId(HttpRequest* request);
	sp<Session> GetSession(const string& key, int timeout = 0);
	void SetSessionId(HttpResponse* response, const string& sid);
	sp<Session> GetLocaleSession(const string& key, int timeout = 0);
	string GetLimitString(DBConnect* dbconn, int pagesize = 1, int page = 0);
	bool IsFileName(const string& str, int minlen = 1, int maxlen = 64);
	bool IsFilePath(const string& str, int minlen = 1, int maxlen = 256);
	bool IsAlnumString(const string& str, int minlen = 1, int maxlen = 64);
	bool IsSeqnoString(const string& str, int minlen = 8, int maxlen = 32);
	void CheckFileName(const string& str, int minlen = 1, int maxlen = 64);
	void CheckFilePath(const string& str, int minlen = 1, int maxlen = 256);
	void CheckAlnumString(const string& str, int minlen = 1, int maxlen = 64);
	void CheckSeqnoString(const string& str, int minlen = 8, int maxlen = 32);

	bool InitDatabase();
	int LoadHttpPlugin(const string& path);

	int SaveParamItem(FileParamItem& item, const string& path);
	int GetFileParamList(vector<FileParamItem>& vec, SmartBuffer& buffer, HttpServer* app, HttpRequest* request, HttpResponse* response);

	int PackJson(sp<QueryResult> rs, JsonElement& json);
	int PackJson(sp<QueryResult> rs, const string& name, JsonElement& json);

	int PackJson(sp<QueryResult> rs, JsonElement& json, function<string(int, const string&)> func);
	int PackJson(sp<QueryResult> rs, const string& name, JsonElement& json, function<string(int, const string&)> func);
};

#ifdef XG_WEBX_CPPSHELL

#define param_string(name) string name = webx::GetAppDataParameter(#name)
#define param_int(name) int name = stdx::atoi(webx::GetAppDataParameter(#name).c_str())
#define param_float(name) double name = stdx::atof(webx::GetAppDataParameter(#name).c_str())

#define head_param_string(name) string name = webx::GetAppDataParameter(#name)
#define head_param_int(name) int name = stdx::atoi(webx::GetAppDataParameter(#name).c_str())
#define head_param_float(name) double name = stdx::atof(webx::GetAppDataParameter(#name).c_str())

#define param_name_string(name) 					\
string name = webx::GetAppDataParameter(#name);		\
if (!webx::IsFileName(name)) stdx::Throw(XG_PARAMERR, "invalid parameter[" + string(#name) + "]")

#define param_path_string(name) 					\
string name = webx::GetAppDataParameter(#name);		\
if (!webx::IsFilePath(name)) stdx::Throw(XG_PARAMERR, "invalid parameter[" + string(#name) + "]")

#define param_seqno_string(name) 					\
string name = webx::GetAppDataParameter(#name);		\
if (!webx::IsSeqnoString(name)) stdx::Throw(XG_PARAMERR, "invalid parameter[" + string(#name) + "]")

#else

extern void SetHttpDestroyFunc(DESTROY_HTTP_CGI_FUNC func);

#define param_string(name) string name = request->getParameter(#name)
#define param_int(name) int name = stdx::atoi(request->getParameter(#name).c_str())
#define param_float(name) double name = stdx::atof(request->getParameter(#name).c_str())

#define head_param_string(name) string name = request->getHeadValue(#name)
#define head_param_int(name) int name = stdx::atoi(request->getHeadValue(#name).c_str())
#define head_param_float(name) double name = stdx::atof(request->getHeadValue(#name).c_str())

#define param_name_string(name) 			\
string name = request->getParameter(#name);	\
if (!webx::IsFileName(name)) stdx::Throw(XG_PARAMERR, "invalid parameter[" + string(#name) + "]")

#define param_path_string(name) 			\
string name = request->getParameter(#name);	\
if (!webx::IsFilePath(name)) stdx::Throw(XG_PARAMERR, "invalid parameter[" + string(#name) + "]")

#define param_seqno_string(name) 			\
string name = request->getParameter(#name);	\
if (!webx::IsSeqnoString(name)) stdx::Throw(XG_PARAMERR, "invalid parameter[" + string(#name) + "]")

#endif

#define START_WEB_APP(APPCLASS)						\
static string _cgipath_;							\
int main(int argc, char* argv[])					\
{													\
	Process::Instance(argc, argv);					\
	const char* cmd = Process::GetCmdParam("-c");	\
	if (cmd && *cmd)								\
	{												\
		string key = cmd;							\
		stdx::tolower(key);							\
		if (key == "getcgipath")					\
		{											\
			puts(_cgipath_.c_str());				\
			return 0;								\
		}											\
		if (key == "getcgipathlist")				\
		{											\
			puts(webx::GetCgiPathList().c_str());	\
			return 0;								\
		}											\
	}												\
	sp<APPCLASS> app = newsp<APPCLASS>();			\
	Process::GetApplication(app.get());				\
	return app->main() ? 0 : XG_ERROR;				\
}

class HttpCgiSetup
{
public:
	static string& GetCgiPath();

public:
	HttpCgiSetup()
	{
		GetCgiPath() = HttpServer::GetWebAppPath("${filename}", DllFile::GetLastPath().c_str());
		HttpServer::Instance()->setCgiAccess(GetCgiPath(), CGI_PRIVATE);
	}
	HttpCgiSetup(int access)
	{
		GetCgiPath() = HttpServer::GetWebAppPath("${filename}", DllFile::GetLastPath().c_str());
		HttpServer::Instance()->setCgiAccess(GetCgiPath(), access);
	}
	HttpCgiSetup(const char* path)
	{
		GetCgiPath() = HttpServer::GetWebAppPath(path, DllFile::GetLastPath().c_str());
		HttpServer::Instance()->setCgiAccess(GetCgiPath(), CGI_PRIVATE);
	}
	HttpCgiSetup(int access, const char* path)
	{
		GetCgiPath() = HttpServer::GetWebAppPath(path, DllFile::GetLastPath().c_str());
		HttpServer::Instance()->setCgiAccess(GetCgiPath(), access);
	}
	HttpCgiSetup(const char* path, int access)
	{
		GetCgiPath() = HttpServer::GetWebAppPath(path, DllFile::GetLastPath().c_str());
		HttpServer::Instance()->setCgiAccess(GetCgiPath(), access);
	}
	HttpCgiSetup(const char* path, const char* extdata)
	{
		GetCgiPath() = HttpServer::GetWebAppPath(path, DllFile::GetLastPath().c_str());
		HttpServer::Instance()->setCgiExtdata(GetCgiPath(), extdata ? extdata : "");
		HttpServer::Instance()->setCgiAccess(GetCgiPath(), CGI_PRIVATE);
	}
	HttpCgiSetup(const char* path, const char* extdata, int access)
	{
		GetCgiPath() = HttpServer::GetWebAppPath(path, DllFile::GetLastPath().c_str());
		HttpServer::Instance()->setCgiExtdata(GetCgiPath(), extdata ? extdata : "");
		HttpServer::Instance()->setCgiAccess(GetCgiPath(), access);
	}
	HttpCgiSetup(const char* path, int access, const char* extdata)
	{
		GetCgiPath() = HttpServer::GetWebAppPath(path, DllFile::GetLastPath().c_str());
		HttpServer::Instance()->setCgiExtdata(GetCgiPath(), extdata ? extdata : "");
		HttpServer::Instance()->setCgiAccess(GetCgiPath(), access);
	}
	HttpCgiSetup(int access, const char* path, const char* extdata)
	{
		GetCgiPath() = HttpServer::GetWebAppPath(path, DllFile::GetLastPath().c_str());
		HttpServer::Instance()->setCgiExtdata(GetCgiPath(), extdata ? extdata : "");
		HttpServer::Instance()->setCgiAccess(GetCgiPath(), access);
	}

public:
	const string& getPath() const
	{
		return GetCgiPath();
	}
};

template<class TYPE> class HttpAppSetup
{
protected:
	string key;

public:
	HttpAppSetup()
	{
		init("${classname}");
	}
	HttpAppSetup(int access)
	{
		init("${classname}", access);
	}
	HttpAppSetup(const char* path)
	{
		init(path);
	}
	HttpAppSetup(int access, const char* path)
	{
		init(path, access);
	}
	HttpAppSetup(const char* path, int access)
	{
		init(path, access);
	}
	HttpAppSetup(const char* path, const char* extdata)
	{
		init(path, CGI_PRIVATE, extdata);
	}
	HttpAppSetup(const char* path, const char* extdata, int access)
	{
		init(path, access, extdata);
	}
	HttpAppSetup(const char* path, int access, const char* extdata)
	{
		init(path, access, extdata);
	}
	HttpAppSetup(int access, const char* path, const char* extdata)
	{
		init(path, access, extdata);
	}
	HttpAppSetup(const char* path, const char* extdata, const char* filename)
	{
		init(path, CGI_PRIVATE, extdata, filename);
	}
	HttpAppSetup(const char* path, int access, const char* extdata, const char* filename)
	{
		init(path, access, extdata, filename);
	}
	void init(const char* path, int access = CGI_PRIVATE, const char* extdata = NULL, const char* filename = Object::GetClassName<TYPE>())
	{
		key = stdx::tolower(HttpServer::GetWebAppPath(path, filename));
		HttpServer::Instance()->setCgiAccess(key, access);
		HttpServer::Instance()->setCgiExtdata(key, extdata ? extdata : "");
		(*webx::GetAppMap())[key] = webx::WebAppData(key, [](){return newsp<TYPE>();});
	}

public:
	const string& getPath() const
	{
		return key;
	}
};

template<class REQUEST, class RESPONSE> class HttpCgidocSetup
{
public:
	HttpCgidocSetup(const char* path, const char* remark)
	{
		HttpServer::Instance()->setCgiDoc(path, REQUEST().toDocString(), RESPONSE().toDocString(), remark);
	}
	HttpCgidocSetup(const string& path, const string& remark)
	{
		HttpServer::Instance()->setCgiDoc(path, REQUEST().toDocString(), RESPONSE().toDocString(), remark);
	}
	HttpCgidocSetup(const char* path, const char* remark, const char* extdata)
	{
		HttpServer::Instance()->setCgiExtdata(path, extdata ? extdata : "");
		HttpServer::Instance()->setCgiDoc(path, REQUEST().toDocString(), RESPONSE().toDocString(), remark ? remark : "");
	}
	HttpCgidocSetup(const string& path, const string& remark, const string& extdata)
	{
		HttpServer::Instance()->setCgiExtdata(path, extdata);
		HttpServer::Instance()->setCgiDoc(path, REQUEST().toDocString(), RESPONSE().toDocString(), remark);
	}
};

#define HTTP_WEBCGI(...) static auto _cgipathsetup_ = newsp<HttpCgiSetup>(__VA_ARGS__);
#define HTTP_WEBAPP(TYPE, ...) static auto _cgipathsetup_ = newsp<HttpAppSetup<TYPE>>(__VA_ARGS__);
#define HTTP_DAILYTASK(TYPE, PATH, TIME) HTTP_WEBAPP(TYPE,PATH,CGI_PROTECT,("dailytask="+stdx::str(TIME)).c_str());
#define HTTP_TIMERTASK(TYPE, PATH, DELAY) HTTP_WEBAPP(TYPE,PATH,CGI_PROTECT,("timertask="+stdx::str(DELAY)).c_str());

#define HTTP_WEBAPI(ACCESS, PATH, FUNC)													\
namespace{class HttpFunc:public webx::ProcessBase{public:HttpFunc(){func=[&]()FUNC;}};	\
static auto _cgipathsetup_ = newsp<HttpAppSetup<HttpFunc>>((char*)PATH,ACCESS,"",__FILE__);}

#define HTTP_APIDOC(REQUEST, RESPONSE, ...) static auto _cgidocsetup_ = newsp<HttpCgidocSetup<REQUEST,RESPONSE>>(_cgipathsetup_->getPath(),__VA_ARGS__);

#define HTTP_PLUGIN_INIT(func)										\
HTTP_WEBCGI(CGI_PRIVATE, "@null")									\
DEFINE_HTTP_CGI_EXPORT_FUNC(HttpProcessBase)						\
EXTERN_DLL_FUNC int HttpPluginInit(){func;return XG_OK;}

#define DEFINE_HTTP_CGI_EXPORT_FUNC(TYPE)							\
EXTERN_DLL_FUNC const char* GetHttpCgiPath()						\
{																	\
	return HttpCgiSetup::GetCgiPath().c_str();						\
}																	\
EXTERN_DLL_FUNC const char* GetHttpCgiPathList()					\
{																	\
	static string path;												\
	if (path.empty()) path = webx::GetCgiPathList();				\
	return path.c_str();											\
}																	\
EXTERN_DLL_FUNC HttpProcessBase* CreateHttpProcessObject()			\
{																	\
	return new TYPE();												\
}																	\
EXTERN_DLL_FUNC void DestroyHttpProcessObject(HttpProcessBase* obj)	\
{																	\
	delete obj;														\
}
///////////////////////////////////////////////////////////
#endif
